home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 17 / CU Amiga Magazine's Super CD-ROM 17 (1997)(EMAP Images)(GB)[!][issue 1997-12].iso / CUCD / Programming / DiceSource / master / Examples / Visual / VMake / command.c next >
C/C++ Source or Header  |  1994-02-01  |  53KB  |  1,492 lines

  1. #include "vmake.h"
  2.  
  3. Prototype int build_command(char *buf, int len, char *template, char *sel);
  4. Prototype void do_command(char *string);
  5. Prototype int save_current(int perm);
  6. Prototype void reset_options(void);
  7. Prototype int set_option(struct G_OBJECT *object,char **argv);
  8. Prototype void exec_command(char *cmd, char *sel);
  9. Prototype void set_workdir(void);
  10.  
  11. void unload_symbols(void);
  12. void load_symbols(void);
  13. void add_list(struct G_LIST *list, char *name, int private);
  14. void add_listitem(struct G_LIST *list, char *name, int private, int dirty);
  15.  
  16. #define MAX_SYMBOL 31
  17. #define LINE_LEN  78
  18. #define MIN_LINE  10
  19. #define SYM_PROJCFG "CFGNAME"
  20.  
  21. static int showcmd;
  22.  
  23. /***********************************************************************************
  24.  * Procedure: get_cursel
  25.  * Synopsis:  name = get_cursel();
  26.  * Purpose:   Return the name associated with the current selection
  27.  ***********************************************************************************/
  28. static char *get_cursel(void)
  29. {
  30.    struct G_OBJECT *object;
  31. #define object_list  ((struct G_LIST   *)object)
  32.  
  33.    /* We need to determine the currently selected file */
  34.    for(object = global.objects; object != NULL; object = object->next)
  35.    {
  36.       if ((object->class == CLASS_LIST) && object_list->sel)
  37.          return(object_list->sel->buf);
  38. #undef object_list
  39.    }
  40.    return("");
  41. }
  42.  
  43. /*****************************************************************************
  44.  * Procedure: GetPubScrName
  45.  * Synopsis:  bool = GetPubScrName(scr, buff)
  46.  * Purpose:   fill the name of public a screen int a buffer, return TRUE
  47.  *            if found
  48.  *****************************************************************************/
  49. int GetPubScrName( struct Screen * scr, UBYTE * namebuff )
  50. {
  51.    struct List        * scrlist;
  52.    struct Node        * node;
  53.  
  54.    namebuff[0] = 0;
  55.  
  56.    if (AslBase) /* need Dos 2.0 or higher for this trick */
  57.       if(scrlist = LockPubScreenList()) 
  58.       {
  59.          /* traverse the public screen node list looking for our screen */
  60.          for ( node = scrlist->lh_Head; node->ln_Succ; node = node->ln_Succ ) 
  61.          {
  62.             if ( ((struct PubScreenNode *)node)->psn_Screen == scr ) 
  63.             {
  64.                strcpy( namebuff, node->ln_Name );
  65.                break;
  66.             }
  67.          }
  68.  
  69.          UnlockPubScreenList();
  70.       }
  71.  
  72.    return( namebuff[0] ? TRUE : FALSE );
  73.  
  74. } // GetPubScrName()
  75.  
  76. /***********************************************************************************
  77.  * Procedure: build_command
  78.  * Synopsis:  rc = build_command(buf, len, template)
  79.  * Purpose:   Construct a command to be issued
  80.  *            The template may have % strings in it where we will substitute:
  81.  *               %s   - Current selection
  82.  *               %f   - The current DMAKEFILE
  83.  *               %o   - An optional selection
  84.  *               %t   - The object module version of a file
  85.  *               %r   - Relative pathname of file
  86.  *               %(x) - The expansion of symbol X
  87.  *               %%   - insert one literal % in the buffer
  88.  ***********************************************************************************/
  89. int build_command(char *buf, int len, char *template, char *sel)
  90. {
  91.    char *name;
  92.    int nlen;
  93.    int makeobj;
  94.    /* note: MAX_FILENAME is greater than MAXPBUBSCREENNAME */
  95.    char exname[MAX_FILENAME+1];
  96.  
  97.    while(*template && len)
  98.    {
  99.       if (*template != '%')
  100.       {
  101.          *buf++ = *template++;
  102.          len--;
  103.       }
  104.       else
  105.       {
  106.          int cvalid = 1; /* Assume valid substitution */
  107.          char req;
  108.          makeobj = 0;
  109.          req = toupper(template[1]);
  110.          template += 2; /* skip over %x */
  111.          name = sel;
  112.          switch(req)
  113.          {
  114.             case 'F':
  115.                {
  116.                   BPTR savelock = 0;
  117.                   if (global.homedir)
  118.                      savelock = CurrentDir(global.homedir);
  119.                   expand_filename(Sym_Lookup(SYM_SCRIPT), exname);
  120.                   name = exname;
  121.                   if (savelock)
  122.                      CurrentDir(savelock);
  123.                   break;
  124.                }
  125.             case '(':
  126.                {
  127.                   char symbol[MAX_SYMBOL+1];
  128.                   int i;
  129.                   i = 0;
  130.                   for(i = 0; *template && (*template != ')'); template++)
  131.                      if (i < MAX_SYMBOL) symbol[i++] = *template;
  132.                   symbol[i] = 0;
  133.                   name = Sym_Lookup(symbol);
  134.                   if (*template == ')') template++;
  135.                   if (!*name)
  136.                   {
  137.                      i = atoi(symbol);
  138.                      if ((i > 0) && (i <= NUM_CONFIG))
  139.                      {
  140.                         name = global.text[CONFIG_BASE-1+i];
  141.                         if (name == NULL) name = "";
  142.                         else
  143.                         {
  144.                            int rc;
  145.  
  146.                            /* We want to recurse to parse out any substitutions     */
  147.                            /* in the string.  Note that in order to prevent runaway */
  148.                            /* recursion, we temporarily kill the config string      */
  149.                            global.text[CONFIG_BASE-1+i] = "";
  150.                            rc = build_command(buf, len, name, sel);
  151.                            global.text[CONFIG_BASE-1+i] = name;
  152.  
  153.                            /* if something went wrong on the recursion, just get    */
  154.                            /* out of here.                                          */
  155.                            if (rc) return(i);
  156.  
  157.                            /* This is a little tricky, what we are doing is cheating*/
  158.                            /* by letting it fill in the buffer with what is already */
  159.                            /* there.  This is harmless (the copy is onto itself) and*/
  160.                            /* allows us to get the length checking done for free    */
  161.                            name = buf;
  162.                         }
  163.                      }
  164.                      else
  165.                      {
  166.                         int max, istart;
  167.                         switch (toupper(*symbol))
  168.                         {
  169.                         case 'T':
  170.                            max = NUM_TEXT;
  171.                            istart = 0;
  172.                            break;
  173.                         case 'C':
  174.                            max = NUM_CONFIG;
  175.                            istart = CONFIG_BASE;
  176.                            break;
  177.                         case 'S':
  178.                            max = NUM_SUBRTN;
  179.                            istart = SUBRTN_BASE;
  180.                            break;
  181.                         default:
  182.                            max = 0;
  183.                            break;
  184.                         }
  185.                         i = atoi(symbol+1);
  186.                         if ((i > 0) && (i <= max))
  187.                         {
  188.                            name = global.text[istart+i-1];
  189.                         }
  190.                      }
  191.                   }
  192.                }
  193.                break;
  194.  
  195.             case 'T':
  196.                {
  197.                   char *p;
  198.                   if (!*name)
  199.                   {
  200.                      p = Sym_Lookup("EXEDIR");
  201.                      name = Sym_Lookup("PROJECT");
  202.                   }
  203.                   else
  204.                   {
  205.                      p = Sym_Lookup("OD");
  206.                      makeobj = 1;
  207.                   }
  208.  
  209.                   /* we need to first copy over the output directory */
  210.                   nlen = strlen(p);
  211.                   if (nlen > len)
  212.                   {
  213.                      request(1, TEXT_BUFFOVFL, NULL, NULL);
  214.                      return(1);
  215.                   }
  216.                   strcpy(buf, p);
  217.                   buf += nlen;
  218.                   len -= nlen;
  219.                }
  220.                break;
  221.  
  222.             case 'N':
  223.                if (global.screen && GetPubScrName(global.screen, exname))
  224.                   name = exname;
  225.                else
  226.                   name = "Workbench";
  227.                break;
  228.  
  229.             case 'R':
  230.             case 'S':
  231.                /* If nothing is selected, and they required a selection, let  */
  232.                /* them know and abort the command                             */
  233.                if (!*sel)
  234.                {
  235.                   request(1, TEXT_NOSEL, NULL, NULL);
  236.                   return(1);
  237.                }
  238.  
  239.                /* Expand pathname for 'S', not for 'R' */
  240.                if (req == 'R') break;
  241.                /* Else fall through to prepend the path to the name */
  242.  
  243.             case 'O':
  244.                expand_filename(sel, exname);
  245.                name = exname;
  246.                break;
  247.             case '%':
  248.                /* interpret "%%" as just "%" and stuff it into the command    */
  249.                /* buffer.  Fall through to default processing for literals.   */
  250.                template += 1;
  251.             default:
  252.                cvalid = 0; /* just copy literally, not a substitution */
  253.          }
  254.          if (cvalid) /* Means we're doing a substitution */
  255.          {
  256.             /* name is what we want to fill the current buffer with */
  257.             nlen = strlen(name);
  258.             if (nlen > len)
  259.             {
  260.                request(1, TEXT_BUFFOVFL, NULL, NULL);
  261.                return(1);
  262.             }
  263.             strcpy(buf, name);
  264.             if (makeobj)
  265.             {
  266.                /* If they requested a output file, we need to convert the */
  267.                /* file to an object file name                             */
  268.                if (nlen > 2 && (buf[nlen-2] == '.'))
  269.                   buf[nlen-1] = 'o';
  270.             }
  271.             buf += nlen;
  272.             len -= nlen;
  273.          }
  274.          else /* Just copy over literally */
  275.          {
  276.             /* just do the '%' here, main loop checks length as well */
  277.             *buf++ = '%';
  278.             template -= 1;  /* back up to char after '%' */
  279.             len--;
  280.          }
  281.       }
  282.    }
  283.    *buf++ = 0;  /* Don't forget to null terminate the result */
  284.    return(0);
  285. }
  286.  
  287. /***********************************************************************************
  288.  * Procedure: parse_keyword
  289.  * Synopsis:  ptr = parse_keyword(str, buf);
  290.  * Purpose:   Parse a keyword into the buffer
  291.  ***********************************************************************************/
  292. char *parse_keyword(char *str, char *buf)
  293. {
  294.    int i;
  295.    /* Gather the string into a buffer */
  296.    i = 0;
  297.    while(*str == ' ') str++;
  298.  
  299.    while((*str) && (*str != ' '))
  300.    {
  301.       buf[i++] = *str++;
  302.       if (i > MAX_SYMBOL) return(str);
  303.    }
  304.    buf[i] = 0;
  305.    while(*str == ' ') str++;
  306.    return(str);
  307. }
  308.  
  309. /***********************************************************************************
  310.  * Procedure: exec_command
  311.  * Synopsis:  exec_command(cmd, selection)
  312.  * Purpose:   Execute the given command using appropriate substitutions
  313.  ***********************************************************************************/
  314. void exec_command(char *cmd, char *sel)
  315. {
  316.  
  317.    go_dir(global.workdir);
  318.  
  319.    if (!build_command(global.cbuf, global.cbufsize, cmd, sel))
  320.    {
  321.       if (showcmd)
  322.          PostLog(global.cbuf);
  323.       IssueCommand(global.cbuf);
  324.    }
  325. }
  326.  
  327. /***********************************************************************************
  328.  * Procedure: send_rexx
  329.  * Synopsis:  send_rexx(cmd)
  330.  * Purpose:   Send the rexx command to the given port name
  331.  ***********************************************************************************/
  332. void send_rexx(char *cmd)
  333. {
  334.    char *res;
  335.    long ec;
  336.    char port[MAX_SYMBOL+1];
  337.  
  338.    go_dir(global.workdir);
  339.  
  340.    if (!build_command(global.cbuf, global.cbufsize, cmd, get_cursel()))
  341.    {
  342.       if (showcmd)
  343.          PostLog(global.cbuf);
  344.       /* Parse out the port name */
  345.       cmd = parse_keyword(global.cbuf, port);
  346.       /* Send the command off to rexx to be processed */
  347.       /* We will wait here until it is complete       */
  348.       PlaceRexxCommandDirect(NULL, port, cmd, &res, &ec);
  349.       if (res != NULL)
  350.       {
  351.          PostLog(res);
  352.          free(res);
  353.       }
  354.    }
  355.  
  356. }
  357.  
  358. /***********************************************************************************
  359.  * Procedure: log_message
  360.  * Synopsis:  log_message(msg, dorexx)
  361.  * Purpose:   Output a message to the session log
  362.  ***********************************************************************************/
  363. void log_message(char *msg, int dorexx)
  364. {
  365.    int gotmsg;
  366.  
  367.    /* set RC in case build_command() fails and doesn't say why */
  368.    if (dorexx)
  369.       global.rexxrc = TEXT_BADPARM;
  370.  
  371.    gotmsg = !build_command(global.cbuf, global.cbufsize, msg, get_cursel());
  372.    if (gotmsg)
  373.       PostLog(global.cbuf);
  374.    else
  375.       PostLog("-------------------");
  376.  
  377.    if (dorexx)
  378.    {
  379.       if (gotmsg)
  380.       {
  381.          /* undo previous default -- build_command() worked */
  382.          global.rexxrc = 0;
  383.          strcpy(global.rexxrs, global.cbuf);
  384.          global.rexxrs[global.cbufsize] = 0;
  385.       }
  386.    }
  387. }
  388.  
  389. /***********************************************************************************
  390.  * Procedure: set_workdir
  391.  * Synopsis:  set_workdir();
  392.  * Purpose:   Set the working directory based on the current directory and symbol
  393.  *            values
  394.  ***********************************************************************************/
  395. void set_workdir(void)
  396. {
  397.    if (test_dirty())
  398.    {
  399.       load_symbols();
  400.    }
  401.  
  402.    go_dir(global.homedir);
  403.    UnLock(global.workdir);
  404.    global.workdir = xlockdir(Sym_Lookup("DIR"));
  405.    go_dir(global.workdir);
  406. }
  407.  
  408. /***********************************************************************************
  409.  * Procedure: save_current
  410.  * Synopsis:  rc = save_current();
  411.  * Purpose:   Save the current file (if it has changed)
  412.  ***********************************************************************************/
  413. int save_current(int perm)
  414. {
  415.    if (test_dirty())
  416.    {
  417.       int rc;
  418.  
  419.       rc = request(0, TEXT_ASKSAVE,
  420.                    Sym_Lookup(SYM_SCRIPT), global.text[TEXT_SAVE]);
  421.  
  422.       if (!rc) return(1);
  423.  
  424.       if (rc == 2)
  425.       {
  426.          load_symbols();
  427.          if (write_file()) return(1);
  428.       }
  429.       mark_clean();
  430.    }
  431.    return(0);
  432. }
  433.  
  434. /***********************************************************************************
  435.  * Procedure: lookup_keyword
  436.  * Synopsis:  kw = lookup_keyword(&p, keystr);
  437.  * Purpose:   Lookup the keyword given and return the position of the next token
  438.  *            (If the keyword was found).  If the keyword wasn't found, -1 is returned
  439.  ***********************************************************************************/
  440. int lookup_keyword(char **bufp, char *keystr)
  441. {
  442.    int i;
  443.    char *p;
  444.    char buf[MAX_SYMBOL+1];
  445.  
  446.    p = parse_keyword(*bufp, buf);
  447.  
  448.    /* Look for the keyword in the table they provided us */
  449.    for(i = 0; *keystr; keystr += strlen(keystr)+1, i++)
  450.    {
  451.       if (!stricmp(buf, keystr))
  452.       {
  453.          /* We got it.  Return the end of the string for them */
  454.          *bufp = p;
  455.          return(i);
  456.       }
  457.    }
  458.  
  459.    /* We didn't find the keyword, so let them know */
  460.    return(-1);
  461. }
  462.  
  463. char cmdlist[] =
  464. #define KEY_TRACE     0
  465.            "TRACE\0" /* Debug the program in the current mode         */
  466. #define KEY_EDIT      1
  467.            "EDIT\0"  /* Invoke the editor on the current file         */
  468. #define KEY_EXEC      2
  469.            "EXEC\0"  /* Execute the given command                     */
  470. #define KEY_NEW       3
  471.            "NEW\0"   /* RESET, Prompt for a new file name             */
  472. #define KEY_QUIT      4
  473.            "QUIT\0"  /* Exit                                          */
  474. #define KEY_READ      5
  475.            "READ\0"  /* Read a file                                   */
  476. #define KEY_SAVE      6
  477.            "SAVE\0"  /* Save file                                     */
  478. #define KEY_FRONT     7
  479.            "FRONT\0" /* Pop Window to the front                       */
  480. #define KEY_BACK      8
  481.            "BACK\0"  /* Send Window to the back                       */
  482. #define KEY_JUMP      9
  483.            "JUMP\0"  /* Jump window to a given screen                 */
  484. #define KEY_LOG      10
  485.            "LOG\0"   /* Output a log message to the screen            */
  486. #define KEY_SCAN     11
  487.            "SCAN\0"  /* Scan the directory for a file pattern list    */
  488. #define KEY_ADDR     12
  489.            "ADDR\0"  /* Send a rexx command to a given port           */
  490. #define KEY_SET      13
  491.            "SET\0"   /* Set a symbol to a given value                 */
  492. #define KEY_CONFIG   14
  493.            "CONFIG\0"/* Change a config string                        */
  494. #define KEY_CALL     15
  495.            "CALL\0"  /* Execute a config string                       */
  496. #define KEY_SELECT   16
  497.            "SELECT\0"/* Select an element in the file list            */
  498. #define KEY_ADD      17
  499.            "ADD\0"   /* Add an element to the list                    */
  500. #define KEY_DEL      18
  501.            "DEL\0"   /* Delete the current item from the list         */
  502. #define KEY_LTOP     19
  503.            "LTOP\0"  /* Select top item in list                       */
  504. #define KEY_LBOT     20
  505.            "LBOT\0"  /* Select bottom item in list                    */
  506. #define KEY_LUP      21
  507.            "LUP\0"   /* Select next item in list                      */
  508. #define KEY_LDN      22
  509.            "LDN\0"   /* Select previous item in list                  */
  510. #define KEY_RECFG    23
  511.            "RECFG\0" /* Read in a config file                         */
  512. #define KEY_REXXI    24
  513.            "REXXI\0" /* Set REXX mode to interactive                  */
  514. ;
  515.  
  516. /***********************************************************************************
  517.  * Procedure: scan_dir
  518.  * Synopsis:  (void)scan_dir();
  519.  * Purpose:   Scan the working directory for all potential files
  520.  ***********************************************************************************/
  521. void scan_dir(char *pat)
  522. {
  523.    FILE *fp;
  524.    char buf[32];
  525.  
  526.    if (!global.filelist) return;
  527.  
  528.    if (!*pat) pat = global.text[CONFIG_FILES];
  529.  
  530.    exec_command("LIST >RAM:TEMPFILE \"%r\" LFORMAT=\"%%s%%s\"", pat);
  531.    if ((fp = fopen("RAM:TEMPFILE", "r")) != NULL)
  532.    {
  533.       while(fgets(buf, 64, fp) != NULL)
  534.       {
  535.          int len;
  536.          len = strlen(buf) - 1;  /* Get rid of the \n */
  537.          while(len && (buf[len-1] == ' ')) len--;
  538.  
  539.          buf[len] = 0; /* Make sure we null terminate it */
  540.          add_listitem(global.filelist, buf, 0, 1);
  541.       }
  542.       fclose(fp);
  543.    }
  544. }
  545.  
  546. /***********************************************************************************
  547.  * Procedure: do_command
  548.  * Synopsis:  do_command(string);
  549.  * Purpose:   Execute the command associated with a given string
  550.  ***********************************************************************************/
  551. void do_command(char *string)
  552. {
  553.    char *p;
  554.    int  cmdnum;
  555.    int  postlog;
  556.    char *callstr;
  557.    int  lclass;
  558.  
  559.    /* EDIT                - Invoke the editor on the current file         */
  560.    /* EXEC cmd            - Execute the given command                     */
  561.    /* NEW     [ file | ?] - RESET, Prompt for a new file name             */
  562.    /* QUIT                - Exit                                          */
  563.    /* READ    [ file | ?] - Read a file                                   */
  564.    /* RUN                 - Execute program in current mode               */
  565.    /* SAVE    [ file | ?] - Save file                                     */
  566.    /* FRONT               - Pop Window to the front                       */
  567.    /* BACK                - Send Window to the back                       */
  568.    /* JUMP [screen]       - Jump Window to a given screen                 */
  569.    /* LOG  [string]       - Output a long message to the screen           */
  570.  
  571.    set_workdir();
  572.    set_busy();
  573.    callstr = NULL;
  574.    postlog = 0;
  575.  
  576.    while (*string)
  577.    {
  578.       int c;
  579.  
  580.       cmdnum = lookup_keyword(&string, cmdlist);
  581.  
  582.       /* Skip over the name to the rest of the command */
  583.       p = string;
  584.  
  585.       /* Locate any trailing semi-colon so we can have multiple commands */
  586.       while(*string && (*string++ != ';'))
  587.          ;
  588.       if ((c = string[-1]) == ';')
  589.          string[-1] = 0;
  590.  
  591.  
  592.       switch(cmdnum)
  593.       {
  594.          case KEY_TRACE: /*             - Toggle the trace state */
  595.             showcmd = !showcmd;
  596.             break;
  597.          case KEY_EXEC:  /* cmd         - Execute a given command               */
  598.             InitSession();
  599.             if (save_current(0)) break;
  600.             exec_command(p, get_cursel());
  601.             break;
  602.          case KEY_NEW:   /* [ file | ?] - RESET, Prompt for a new file name     */
  603.             {
  604.                char *ext;
  605.                int  len, elen;
  606.                char buf[MAX_FILENAME+1];
  607.  
  608.                global.rexxrc = TEXT_BADPROJ; /* unless another error occurs */
  609.                if (save_current(1)) break;
  610.                if (!get_filename(p, buf, FALSE)) break;
  611.  
  612.                /* Now we need to see if the project already exists and warn them */
  613.                /* that they will be overwriting it.                              */
  614.                {
  615.                   BPTR lock;
  616.                   lock = Lock(buf, ACCESS_READ);
  617.                   if (lock != NULL)
  618.                   {
  619.                      UnLock(lock);
  620.                      if (!request(0, TEXT_ASKKILL, buf, NULL))
  621.                      {
  622.                         break;
  623.                      }
  624.                   }
  625.                }
  626.  
  627.                /* no errors, no cancellations */
  628.                global.rexxrc = 0;
  629.                commit_filename(buf);
  630.                set_gadgets(0);
  631.                reset_options();
  632.                /* indicate that a new icon will be required                */
  633.                global.oldproject = 0;
  634.                /* really trying to load a project, need to fix up ghosting */
  635.                global.unghost = 1;
  636.  
  637.                /* If they didn't put an extension on the end of it, we want to     */
  638.                /* automatically do that for them.  We should also seed the default */
  639.                /* project name for them.                                           */
  640.                p = Sym_Lookup(SYM_SCRIPT);
  641.                ext = global.text[CONFIG_EXT];
  642.                elen = strlen(ext);
  643.                len = strlen(p) - elen;
  644.  
  645.                /* If the name is shorter than the extension OR it doesn't end with */
  646.                /* the extension, then we will put the extension on for them        */
  647.                if ((len < 0) || stricmp(p+len, ext))
  648.                {
  649.                   Sym_Set("PROJECT", p, NULL);
  650.                   Sym_Set(SYM_SCRIPT, NULL, ext);
  651.                   strcpy(global.filename, Sym_Lookup(SYM_SCRIPT));
  652.                }
  653.                else
  654.                {
  655.                   p[len] = 0;
  656.                   Sym_Set("PROJECT", p, NULL);
  657.                   p[len] = *ext;
  658.                }
  659.                Sym_Set("TYPE", "Normal", NULL);
  660.                Sym_Set("CFLAGS", "-R -// -f -2.0 -d1", NULL);
  661.  
  662.                unload_symbols();
  663.                set_gadgets(1);
  664.             }
  665.             break;
  666.          case KEY_QUIT:  /*             - Exit                                  */
  667.             if (!save_current(1))
  668.                global.done = 1;
  669.             break;
  670.          case KEY_EDIT:  /*             - Invoke the editor on the current file */
  671.             global.rexxrc = TEXT_NOSEL; /* default error for interactive REXX */
  672.             if (save_current(0)) break;
  673.             {
  674.                struct stat stat_buf;
  675.                char *sel;
  676.  
  677.                /* If the file is read-only, we want to see if we can check it out  */
  678.                p = global.text[CONFIG_CO];
  679.                sel = get_cursel();
  680.                if (sel && (stat(sel, &stat_buf) >= 0))
  681.                {
  682.                   if (!(stat_buf.st_mode & S_IWRITE))
  683.                   {
  684.                      int rc;
  685.  
  686.                      /* The file exists, but is read-only.  See if they want us */
  687.                      /* to check it out for them to work on                     */
  688.                      rc = request(0, TEXT_ASKCO, sel, global.text[TEXT_CO]);
  689.                      if (rc == 0) break;  /* Don't do the edit */
  690.                      if (rc == 2)
  691.                      {
  692.                         exec_command(global.text[CONFIG_CO], sel);
  693.                      }
  694.                   }
  695.                }
  696.                /* not aborting, can't be any errors... */
  697.                global.rexxrc = 0;
  698.                /* if EDPROJ config string is filled, we can do special      */
  699.                /* handling for project file.                                */
  700.                if ((global.text[CONFIG_EDPROJ] == 0) || 
  701.                    (stricmp(sel, Sym_Lookup(SYM_SCRIPT))))
  702.                {
  703.                   exec_command(global.text[CONFIG_EDIT], sel);
  704.                   break;
  705.                }
  706.                else
  707.                {
  708.                   exec_command(global.text[CONFIG_EDPROJ], sel);
  709.                  if (sel && (stat(sel, &stat_buf) < 0))
  710.                  {
  711.                     /* Don't try to read if it doesn't exist */
  712.                     break;
  713.                  }
  714.                }
  715.             }
  716.             /* Fall through to READ to use modified project file             */
  717.             p = ""; /* READ will default to default to current project       */
  718.          case KEY_READ:  /* [ file | ?] - Read a file                        */
  719.             {
  720.                char *symcfg, *symnew;
  721.                char rbuff[200];
  722.                char buf[MAX_FILENAME+1];
  723.                struct stat stat_buf;
  724.  
  725.                global.rexxrc = TEXT_BADPROJ; /* just in case... */
  726.                if (save_current(1)) break;
  727.                if (build_command(rbuff, 200, p, get_cursel())) break;
  728.  
  729.                p = rbuff;
  730.                if (!get_filename(p, buf, FALSE)) break;
  731.                if (*buf && (stat(buf, &stat_buf) < 0))
  732.                {
  733.                   request(1, TEXT_BADFILE, buf, NULL);
  734.                   break;
  735.                }
  736.                commit_filename(buf);
  737.                set_gadgets(0);
  738.                reset_options();
  739.                if (read_file())
  740.                {
  741.                   /* For some reason we couldn't read the file,       */
  742.                   /* just reset all the options to be a null value.   */
  743.                   reset_options();
  744.                   commit_filename("");
  745.                }
  746.                else
  747.                {
  748.                   global.rexxrc = 0;     /* Everything's hunky dory now */
  749.                   /* we're really trying to do it, lock all the gadgets */
  750.                   global.unghost = 1;
  751.                }
  752.                global.oldproject = 1;
  753.  
  754.                /* now see if we need to change the config file          */
  755.                symcfg = Sym_Lookup(SYM_CONFIG);
  756.                symnew = Sym_Lookup(SYM_PROJCFG);
  757.                /* if the project doesn't specify a config file          */
  758.                if (!symnew || (symnew[0] == '\0'))
  759.                   /* revert to original default                         */
  760.                   symnew = Sym_Lookup(SYM_ORIG_CFG);
  761.                /* if configuration file name has changed                */
  762.                if (stricmp(symcfg, symnew))
  763.                {
  764.                   Sym_Set(SYM_CONFIG, symnew, 0);
  765.                   global.newscreen = 1; /* configuration redefines window */
  766.                   global.parsefail = parse_config(symnew);
  767.                   if (global.parsefail)
  768.                   {
  769.                      global.rexxrc = TEXT_BADPROJ;
  770.                      break;   /* error display in title bar             */
  771.                   }
  772.                }
  773.  
  774.                unload_symbols();
  775.                /* If we're about to redisplay the window, defer         */
  776.                /* repainting the gadgets until then                     */
  777.                if (!global.newscreen)
  778.                   set_gadgets(1);
  779.                break;
  780.             }
  781.          case KEY_SAVE:  /* [ file | ?] - Save file                     */
  782.             {
  783.                int  changed;
  784.                char *ext;
  785.                int  len, elen;
  786.                char svproj[MAX_FILENAME+1];
  787.                char newproj[MAX_FILENAME+1];
  788.  
  789.                global.rexxrc = TEXT_BADPROJ; /* in case of unnamed perils */
  790.                /* save project name in case they change it */
  791.                strncpy(svproj, Sym_Lookup(SYM_SCRIPT), MAX_FILENAME);
  792.                svproj[MAX_FILENAME] = '\0';
  793.                if (!get_filename(p, newproj, TRUE)) 
  794.                   break;
  795.                /* Now see if they have chosen a new name that already exists */
  796.                /* and warn them that they will be overwriting it.            */
  797.                changed = stricmp(svproj, newproj);
  798.                if (changed)
  799.                {
  800.                   BPTR lock;
  801.  
  802.                   lock = Lock(newproj, ACCESS_READ);
  803.                   if (lock != NULL)
  804.                   {
  805.                      UnLock(lock);
  806.                      if (!request(0, TEXT_ASKKILL, newproj, NULL))
  807.                      {
  808.                         break;
  809.                      }
  810.                   }
  811.                   /* indicate that a new icon will be required               */
  812.                   global.oldproject = 0;
  813.                }
  814.  
  815.                /* looks like they want to go through with this               */
  816.                global.rexxrc = 0;  /* didn't fail, don't lie to rexx         */
  817.  
  818.                /* If they didn't put an extension on the end of it, we want  */
  819.                /* to automatically do that for them.                         */
  820.                ext = global.text[CONFIG_EXT];
  821.                len = strlen(newproj);
  822.                elen = strlen(ext);
  823.  
  824.                /* If the name is shorter than the extension OR it doesn't end with */
  825.                /* the extension, then we will put the extension on for them        */
  826.                if ((len < elen) || stricmp(newproj+len-elen, ext))
  827.                {
  828.                   strncat(newproj, ext, MAX_FILENAME-len);
  829.                   newproj[MAX_FILENAME] = 0;
  830.                }
  831.                commit_filename(newproj);
  832.  
  833.                (void)test_dirty();
  834.                load_symbols();
  835.                if (!write_file())
  836.                {
  837.                   /* JAT's usual negative logic -- means it worked           */
  838.                   if (changed)
  839.                   {
  840.                      /* Select the old project.                              */
  841.                      handle_list(global.filelist, NULL, CLASS_SELECT, svproj, 0);
  842.                      if (global.filelist->sel != NULL) /* we got it */
  843.                      {
  844.                         /* If it was in the list, delete it                  */
  845.                         handle_list(global.filelist, NULL, CLASS_DEL, NULL, 0);
  846.                      }
  847.                      /* Now put the new project at the end or the list       */
  848.                      set_gadgets(0);
  849.                      add_listitem(global.filelist, newproj, 1, 0);
  850.                      set_gadgets(1);
  851.                   }
  852.                   /* Now the current state is saved in the new project file. */
  853.                   mark_clean();
  854.                }
  855.  
  856.                break;
  857.             }
  858.          case KEY_ADDR:  /* port cmd    - Send an arexx command to a given port */
  859.             InitSession();
  860.             if (save_current(0)) break;
  861.             send_rexx(p);
  862.             break;
  863.          case KEY_FRONT: /*             - Pop Window to the front               */
  864.             WindowToFront(global.window);
  865.             ScreenToFront(global.window->WScreen);
  866.             ActivateWindow(global.window);
  867.             break;
  868.          case KEY_BACK:  /*             - Send Window to the back               */
  869.             WindowToBack(global.window);
  870.             break;
  871.          case KEY_JUMP:  /* [screen]    - Jump Window to a given screen         */
  872.             /* This should only be done for 2.0..... */
  873.             if (AslBase)
  874.             {
  875.                char buf[MAXPUBSCREENNAME+1];
  876.                if (*p == 0)
  877.                {
  878.                   if (NextPubScreen(global.window->WScreen, buf))
  879.                      p = buf;
  880.                }
  881.                if (p)
  882.                   global.screen = LockPubScreen(p);
  883.                else
  884.                   global.screen = 0;
  885.                /* force main event loop to close & reopen the window */
  886.                global.newscreen = 1;
  887.                break;
  888.             }
  889.             request(1, TEXT_BADCMD, "--- JUMP Dos 1.3---", NULL);
  890.             break;
  891.          case KEY_RECFG:
  892.             if (save_current(0) == 0) /* didn't change their mind? */
  893.             {
  894.                char cfname[MAX_FILENAME+1];
  895.                char *cfp = p;
  896.  
  897.                if (*cfp == 0)
  898.                   if (get_work_filename(cfname, CONFIG_CFGFPAT))
  899.                      cfp = cfname;
  900.  
  901.                if (*cfp != 0) /* got a name from somewhere */
  902.                {
  903.                   Sym_Set(SYM_CONFIG, cfp, 0);
  904.                   global.newscreen = 1; /* configuration redefines window */
  905.                   set_gadgets(0);
  906.                   load_symbols();
  907.                   global.parsefail = parse_config(cfp);
  908.                   if (!global.parsefail)
  909.                   {
  910.                      unload_symbols();
  911.                      Sym_Set(SYM_PROJCFG, cfp, 0);
  912.                      global.dirtysym = DOSTRUE;
  913.                   }
  914.                }
  915.             }
  916.             break;
  917.          case KEY_LOG:   /* message     - Output message to the log             */
  918.             log_message("\n---------------------------", 0);
  919.             log_message(p, global.rexxmsgs);
  920.             postlog = 1;
  921.             break;
  922.          case KEY_SCAN:  /*             - Scan for a list of files              */
  923.             set_gadgets(0);
  924.             scan_dir(p);
  925.             set_gadgets(1);
  926.             break;
  927.          case KEY_SET:
  928.             {
  929.                char buf[MAX_SYMBOL+1];
  930.  
  931.                p = parse_keyword(p, buf);
  932.                if (buf[0] == 0)
  933.                {
  934.                   request(1, TEXT_BADPARM, NULL, NULL);
  935.                   break;                  
  936.                }
  937.                Sym_Set(buf, p, NULL);
  938.                if (buf[0] != '_')
  939.                   global.dirtysym = DOSTRUE; /* to flag save_current()  */
  940.             }
  941.             break;
  942.          case KEY_CONFIG:
  943.             {
  944.                char buf[MAX_SYMBOL+1];
  945.  
  946.                p = parse_keyword(p, buf);
  947.  
  948.                cmdnum = atoi(buf);
  949.                if ((cmdnum > 0) && (cmdnum <= NUM_CONFIG))
  950.                {
  951.                   if (global.text[CONFIG_BASE-1+cmdnum])
  952.                      free(global.text[CONFIG_BASE-1+cmdnum]);
  953.                   global.text[CONFIG_BASE-1+cmdnum] = strdup(p);
  954.                }
  955.                else
  956.                   request(1, TEXT_BADPARM, NULL, NULL);
  957.             }
  958.             break;
  959.          case KEY_SELECT:
  960.             /* Select the given name. */
  961.             handle_list(global.filelist, NULL, CLASS_SELECT, p, 0);
  962.             if (global.filelist->sel == NULL) /* we didn't select anything */
  963.                if (*p != '\0') /* and we weren't just trying to deselect   */
  964.                   request(1, TEXT_SELFAIL, p, NULL);
  965.             break;
  966.          case KEY_LTOP:
  967.             lclass = CLASS_LTOP;
  968.             goto listmove;
  969.          case KEY_LBOT:
  970.             lclass = CLASS_LBOT;
  971.             goto listmove;
  972.          case KEY_LUP:
  973.             lclass = CLASS_LUP;
  974.             goto listmove;
  975.          case KEY_LDN:
  976.             lclass = CLASS_LDN;
  977.       listmove:
  978.             handle_list(global.filelist, NULL, lclass, p, 0);
  979.             if (global.inrexx) /* don't nag but do rexx error codes        */ 
  980.                if (global.filelist->sel == NULL) /* didn't select anything */
  981.                   request(1, TEXT_SELFAIL, p, NULL);
  982.             break;
  983.          case KEY_ADD:
  984.             /* add an item to the file list, can specify name with command */
  985.             handle_list(global.filelist, NULL, CLASS_ADD, p, 0);
  986.             break;
  987.          case KEY_DEL:
  988.             /* Delete the current selection from the file list             */
  989.             if (global.inrexx) /* error msg helpful in rexx, else irritating */
  990.             { 
  991.                if (global.filelist->sel == NULL) /* can't delete nothing   */
  992.                {
  993.                   request(1, TEXT_NOSEL, NULL, NULL);
  994.                   break; /* so don't call delete routine                   */
  995.                }
  996.                if (*p != '\0') /* DEl doesn't take parameters              */
  997.                {
  998.                   request(1, TEXT_BADPARM, NULL, NULL);
  999.                   break; /* so don't call delete routine                   */
  1000.                }
  1001.             }
  1002.             handle_list(global.filelist, NULL, CLASS_DEL, p, 0);
  1003.             break;
  1004.          case KEY_REXXI:
  1005.             /* set REXX interactive mode on or off                         */
  1006.             if (!stricmp(p, "on"))
  1007.             {
  1008.                /* Interactive, allows requesters */
  1009.                global.rexxinter = 1;
  1010.                Sym_Set(SYM_REXXINTER, "ON", NULL);
  1011.             }
  1012.             else
  1013.             {
  1014.                if (!stricmp(p, "off"))
  1015.                {
  1016.                   /* set REXX mode to fail instead of displaying requesters */
  1017.                   global.rexxinter = 0;
  1018.                   Sym_Set(SYM_REXXINTER, "OFF", NULL);
  1019.                }
  1020.                else 
  1021.                {
  1022.                   request(1, TEXT_BADRMODE, p, NULL);
  1023.                }
  1024.             }
  1025.             break;
  1026.          case KEY_CALL:
  1027.             if (callstr == NULL)
  1028.             {
  1029.                char buf[MAX_SYMBOL+1];
  1030.  
  1031.                p = parse_keyword(p, buf);
  1032.  
  1033.                cmdnum = atoi(buf);
  1034.                if ((cmdnum > 0)           &&
  1035.                    (cmdnum <= NUM_SUBRTN) &&
  1036.                    ((p = global.text[cmdnum+SUBRTN_BASE-1]) != NULL) &&
  1037.                    *p)
  1038.                {
  1039.                   string[-1] = c;
  1040.                   callstr = string;
  1041.                   string = p;
  1042.                   continue;
  1043.                }
  1044.                break;
  1045.             }
  1046.          default:
  1047.             /* We have an invalid option, ignore the command and let them know  */
  1048.             /* about the problem with the option.                               */
  1049.             request(1, TEXT_BADCMD, p, NULL);
  1050.             /* We also don't want to process any more commands...               */
  1051.             string[-1] = c;
  1052.             goto doneall;
  1053.       }
  1054.       string[-1] = c;
  1055.       if ((*string == '\0') && (callstr != NULL))
  1056.       {
  1057.          string = callstr;
  1058.          callstr = NULL;
  1059.       }
  1060.    }
  1061.  
  1062. doneall:
  1063.    if (postlog)
  1064.    {
  1065.       log_message("---------------------------", 0);
  1066.       if (!global.inrexx)
  1067.          ActivateWindow(global.window);
  1068.    }
  1069.    set_idle();
  1070. }
  1071.  
  1072. /***********************************************************************************
  1073.  * Procedure: reset_options
  1074.  * Synopsis:  reset_options()
  1075.  * Purpose:   Reset all the options to the default values
  1076.  ***********************************************************************************/
  1077. void reset_options(void)
  1078. {
  1079. #define object_list  ((struct G_LIST *)object)
  1080. #define object_str   ((struct G_STRING *)object)
  1081. #define object_cycle ((struct G_CYCLE *)object)
  1082.    struct G_OBJECT *object;
  1083.    char *p;
  1084.  
  1085.    /* First make sure that all the right symbols are in the symbol table in the */
  1086.    /* correct order.  Since the symbol table routines guarentee that it will    */
  1087.    /* keep the order of the entries once they are there, we only need to touch  */
  1088.    /* the order once to keep it correct for the dmakefile.                      */
  1089.    Sym_Clear();
  1090.  
  1091.    p = "PROJECT\0"
  1092.        "DIR\0"
  1093.        "SRCS\0"
  1094.        "HDRS\0"
  1095.        "EXTRAS\0"
  1096.        "PDEFAULT\0"
  1097.        "EXEDIR\0"
  1098.        "OD\0"
  1099.        "PRECOMP\0"
  1100.        "TYPE\0"
  1101.        "RUN\0"
  1102.        "CLIARGS\0"
  1103.        "CFLAGS\0";
  1104.  
  1105.    while(*p)
  1106.    {
  1107.       Sym_Set(p, NULL, "");
  1108.       p += strlen(p)+1;
  1109.    }
  1110.  
  1111.    object = global.objects;
  1112.  
  1113.    while(object != NULL)
  1114.    {
  1115.       switch(object->class)
  1116.       {
  1117.          case CLASS_STRING:
  1118.             object_str->buf[0] = 0;
  1119.             break;
  1120.          case CLASS_CYCLE:
  1121.             {
  1122.                struct G_VALUE *val;
  1123.  
  1124.                val = object_cycle->curval = object_cycle->values;
  1125.                while(val != NULL)
  1126.                {
  1127.                   if (val->string)
  1128.                      val->string->buf[0] = 0;
  1129.                   val = val->next;
  1130.                }
  1131.             }
  1132.             break;
  1133.          case CLASS_LIST:
  1134.             {
  1135.                struct G_ENTRY *ent;
  1136.                ent = object_list->first;
  1137.                object_list->first = object_list->top = NULL;
  1138.                object_list->sel = NULL;
  1139.                object_list->maxent = 0;
  1140.  
  1141.                while(ent != NULL)
  1142.                {
  1143.                   struct G_ENTRY *nextent;
  1144.  
  1145.                   nextent = (struct G_ENTRY *)ent->base.next;
  1146.                   free_mem(ent, sizeof(struct G_ENTRY));
  1147.                   ent = nextent;
  1148.                }
  1149.             }
  1150.             break;
  1151.          case CLASS_BUTTON:
  1152.             break;
  1153.          default:
  1154.             object->state = 0;
  1155.             break;
  1156.       }
  1157.       object = object->next;
  1158.    }
  1159. #undef object_list
  1160. #undef object_str
  1161. #undef object_cycle
  1162.    mark_clean();
  1163.  
  1164.    UnLock(global.workdir);
  1165.    global.workdir = 0;
  1166. }
  1167.  
  1168. /***********************************************************************************
  1169.  * Procedure: unload_symbols
  1170.  * Synopsis:  unload_symbols();
  1171.  * Purpose:   Unload the symbols into the objects
  1172.  ***********************************************************************************/
  1173. void unload_symbols()
  1174. {
  1175.    struct G_OBJECT *object, *savelist;
  1176.    char *p;
  1177.  
  1178. #define object_list  ((struct G_LIST   *)object)
  1179. #define object_str   ((struct G_STRING *)object)
  1180. #define object_check ((struct G_CHECK  *)object)
  1181. #define object_cycle ((struct G_CYCLE  *)object)
  1182.  
  1183.    for(object = global.objects; object != NULL; object = object->next)
  1184.    {
  1185.       switch(object->class)
  1186.       {
  1187.          case CLASS_STRING:
  1188.             strcpy(object_str->buf, Sym_Lookup(object_str->option));
  1189.             break;
  1190.          case CLASS_CYCLE:
  1191.             {
  1192.                char buf[MAX_SYMBOL+1];
  1193.                struct G_VALUE *val;
  1194.  
  1195.                for(val = object_cycle->values;
  1196.                    val != NULL;
  1197.                    val = (struct G_VALUE *)val->next)
  1198.                {
  1199.                   char *arg;
  1200.                   /* Gather the current name into a buffer */
  1201.                   p = parse_keyword(val->option, buf);
  1202.                   arg = Sym_Lookup(buf);
  1203.                   if (val->string)
  1204.                   {
  1205.                      /* If the next character is a % sign, we have an exact */
  1206.                      /* match that we can just fill in.   Otherwise it is   */
  1207.                      /* the name of a string that we should lookup for the  */
  1208.                      /* final symbol to fill it in with                     */
  1209.                      if (*p != '%')
  1210.                      {
  1211.                         /* Parse out the next token - this is the secondary */
  1212.                         /* Mode.  This must match the value of the primary  */
  1213.                         /* option if we are to take it                      */
  1214.                         parse_keyword(p, buf);
  1215.                         if (strcmp(buf, arg)) continue;  /* Sorry, try the next one */
  1216.  
  1217.                         /* We have a match, look up the substitution value  */
  1218.                         /* for the string buffer                            */
  1219.                         arg = Sym_Lookup(buf);
  1220.                      }
  1221.                      /* Copy the string into the object cycle buffer */
  1222.                      strcpy(val->string->buf, arg);
  1223.                      object_cycle->curval = val;
  1224.                      break;
  1225.                   }
  1226.                   else
  1227.                   {
  1228.                      /* Not a string entry.  It must match exactly */
  1229.                      if (!strcmp(p, arg))
  1230.                      {
  1231.                         object_cycle->curval = val;
  1232.                         break;
  1233.                      }
  1234.                   }
  1235.                }
  1236.             }
  1237.             break;
  1238.          case CLASS_LIST:
  1239.             if (!stricmp(object_list->option, "Files"))
  1240.             {
  1241.                savelist = object;
  1242.                /* Populate it with all the strings from the list */
  1243.                add_list(object_list, Sym_Lookup("SRCS"),      0);
  1244.                add_list(object_list, Sym_Lookup("HDRS"),      0);
  1245.                add_list(object_list, Sym_Lookup("EXTRAS"),    0);
  1246.                add_list(object_list, Sym_Lookup(SYM_SCRIPT),  1);
  1247.             }
  1248.             else
  1249.             {
  1250.                add_list(object_list, Sym_Lookup(object_list->option), 0);
  1251.             }
  1252.             break;
  1253.       }
  1254.    }
  1255.  
  1256.    mark_clean();
  1257. #undef object_check
  1258. #undef object_list
  1259. #undef object_str
  1260. #undef object_cycle
  1261. }
  1262.  
  1263. /***********************************************************************************
  1264.  * Procedure: load_symbols
  1265.  * Synopsis:  load_symbols();
  1266.  * Purpose:   load the symbols from the current objects
  1267.  ***********************************************************************************/
  1268. void load_symbols()
  1269. {
  1270.    struct G_OBJECT *object;
  1271.    char *p, *arg;
  1272.  
  1273. #define object_list  ((struct G_LIST   *)object)
  1274. #define object_str   ((struct G_STRING *)object)
  1275. #define object_check ((struct G_CHECK  *)object)
  1276. #define object_cycle ((struct G_CYCLE  *)object)
  1277.  
  1278.    for(object = global.objects; object != NULL; object = object->next)
  1279.    {
  1280.       /* Don't even bother with this object if it hasn't changed */
  1281.       if (!object->state & DIRTY_BIT) continue;
  1282.  
  1283.       switch(object->class)
  1284.       {
  1285.          case CLASS_STRING:
  1286.             Sym_Set(object_str->option, object_str->buf, NULL);
  1287.             break;
  1288.  
  1289.          case CLASS_CYCLE:
  1290.             {
  1291.                struct G_VALUE *val;
  1292.                char buf[MAX_SYMBOL+1];
  1293.                char sec[MAX_SYMBOL+1];
  1294.  
  1295.                val = object_cycle->curval;
  1296.                p = parse_keyword(val->option, buf);
  1297.  
  1298.                if (val->string)
  1299.                {
  1300.                   /* If the next token is a '%', we just use the string buffer */
  1301.                   /* as the result value                                       */
  1302.                   if (*p == '%')
  1303.                   {
  1304.                      p = val->string->buf;
  1305.                   }
  1306.                   else
  1307.                   {
  1308.                      /* Special case of a cascade definition */
  1309.                      /* We need to set the primary definition to the secondary */
  1310.                      /* name and the secondary definition to the string        */
  1311.                      parse_keyword(p, sec);
  1312.                      Sym_Set(sec, val->string->buf, NULL);
  1313.                      p = sec;
  1314.                   }
  1315.                }
  1316.                Sym_Set(buf, p, NULL);
  1317.  
  1318.                /* Handle the one special case that we know about:  */
  1319.                /*   TYPE <whatever> causes us to read in a new script */
  1320.                if (!strcmp(buf, "TYPE"))
  1321.                   read_script();
  1322.             }
  1323.             break;
  1324.  
  1325.          case CLASS_LIST:
  1326.             {
  1327.                int firsttime;
  1328.                struct G_ENTRY *ent;
  1329.                Sym_Set("EXTRAS", "", NULL);
  1330.                Sym_Set("SRCS",   "", NULL);
  1331.                Sym_Set("HDRS",   "", NULL);
  1332.                /* Files                                                                           */
  1333.                /*  - List of C and O files.  Always relative to the current directory             */
  1334.                /*  - Generates the SRCS and HDRS directories by splitting out based on extension. */
  1335.                /*  - Things ending in .C or .A go into SRCS                                       */
  1336.                /*  - Things ending in .H go into HDRS                                             */
  1337.                /*  - All others go into FILES_ext where ext is the extension of the file          */
  1338.                for(firsttime = 1, ent = object_list->first; ent != NULL;
  1339.                    ent = (struct G_ENTRY *)ent->base.next, firsttime = 0)
  1340.                {
  1341.                   arg = "EXTRAS";
  1342.                   p = strrchr(ent->buf, '.');
  1343.                   if (p != NULL)
  1344.                   {
  1345.                      if ((!stricmp(p, ".c")) || (!stricmp(p, ".a"))) arg = "SRCS";
  1346.                      else if (!stricmp(p, ".h"))
  1347.                      {
  1348.                         char *t;
  1349.                         t = strrchr(ent->buf, '/');
  1350.                         if (t == NULL) t = strrchr(ent->buf, ':');
  1351.                         if (t == NULL) t = ent->buf;
  1352.                         arg = "HDRS";
  1353.                         if (p > t)
  1354.                         {
  1355.                            *p = 0;
  1356.                            if (!stricmp(t, Sym_Lookup("PROJECT")))
  1357.                            {
  1358.                               *p = '.';
  1359.                               Sym_Set("PDEFAULT", ent->buf, NULL);
  1360.                            }
  1361.                            *p = '.';
  1362.                         }
  1363.                      }
  1364.                   }
  1365.                   /* Make sure that we don't put the script in there more than */
  1366.                   /* once......  It is already present as the DMAKEFILE one    */
  1367.                   if (!(ent->base.state & STATE_MASK))
  1368.                   {
  1369.                      if (stricmp(object_list->option, "Files"))
  1370.                         arg = object_list->option;
  1371.  
  1372.                      if (*Sym_Lookup(arg)) Sym_Set(arg, NULL, " ");
  1373.  
  1374.                      /* If the filename has spaces, we need to put it in quotes */
  1375.                      if (strchr(ent->buf, ' '))
  1376.                      {
  1377.                         Sym_Set(arg, NULL, "\'");
  1378.                         Sym_Set(arg, NULL, ent->buf);
  1379.                         Sym_Set(arg, NULL, "\'");
  1380.                      }
  1381.                      else
  1382.                      {
  1383.                         Sym_Set(arg, NULL, ent->buf);
  1384.                      }
  1385.                   }
  1386.                }
  1387.             }
  1388.             break;
  1389.       }
  1390.    }
  1391.  
  1392. #undef object_check
  1393. #undef object_list
  1394. #undef object_str
  1395. #undef object_cycle
  1396.  
  1397.    /* A couple of symbols MUST be a directory with the appropriate */
  1398.    /* Directory separator character on the end of it               */
  1399.    /*   DIR                                                        */
  1400.    /*   EXEDIR                                                     */
  1401.    /*   OD                                                         */
  1402.    p = "DIR\0"
  1403.        "EXEDIR\0"
  1404.        "OD\0";
  1405.    for (;*p; p += strlen(p)+1)
  1406.    {
  1407.       char *val;
  1408.       int len;
  1409.  
  1410.       val = Sym_Lookup(p);
  1411.  
  1412.       len = strlen(val);
  1413.       if (len && (val[len-1] != ':') && (val[len-1] != '/'))
  1414.          Sym_Set(p, NULL, "/");
  1415.    }
  1416. }
  1417.  
  1418. /***********************************************************************************
  1419.  * Procedure: add_list
  1420.  * Synopsis:  add_list(list, name)
  1421.  * Purpose:   Add all the filenames in the named symbol table into the list object
  1422.  ***********************************************************************************/
  1423. void add_list(struct G_LIST *list,
  1424.               char *name,
  1425.               int private)
  1426. {
  1427.    char buf[MAX_STRING+1];
  1428.    int pos, c, state;
  1429.  
  1430.    pos = 0;
  1431.    state = 0;
  1432.  
  1433.    do
  1434.    {
  1435.       c = *name++;
  1436.       if (((state == 1) && (c == '\'')) || (pos && (c == ' ' || c == 0)))
  1437.       {
  1438.          buf[pos] = 0;
  1439.          add_listitem(list, buf, private, 0);
  1440.          state = 0;
  1441.          pos = 0;
  1442.       }
  1443.       else
  1444.       {
  1445.          if (c == '\'')     state = 1;
  1446.          else if (state || (c != ' ')) buf[pos++] = c;
  1447.          if (pos == MAX_STRING) pos = MAX_STRING-1;
  1448.       }
  1449.    } while(c);
  1450. }
  1451.  
  1452. /***********************************************************************************
  1453.  * Procedure: add_listitem
  1454.  * Synopsis:  add_listitem(list, name, private, dirty)
  1455.  * Purpose:   Add the given name into the current list
  1456.  ***********************************************************************************/
  1457. void add_listitem(struct G_LIST *list, char *name, int private, int dirty)
  1458. {
  1459.    struct G_ENTRY *ent, *prevent;
  1460.  
  1461.    ent = get_mem(sizeof(struct G_ENTRY));
  1462.    if (ent == NULL) return;
  1463.  
  1464.    strcpy(ent->buf, name);
  1465.    ent->base.state = private;
  1466.    if (dirty)
  1467.       list->base.state |= DIRTY_BIT;
  1468.  
  1469.    /* Figure out where to add the name */
  1470.  
  1471.    /* link the new name into the list */
  1472.    if (list->first == NULL)
  1473.    {
  1474.       list->top = list->first = ent;
  1475.    }
  1476.    else
  1477.    {
  1478.       int i;
  1479.       for (i = 0, prevent = list->first; prevent->base.next;
  1480.            prevent = (struct G_ENTRY *)prevent->base.next, i++)
  1481.       {
  1482.          /* Make sure it isn't already in the list */
  1483.          if (!stricmp(prevent->buf, name))
  1484.          {
  1485.             free_mem(ent, sizeof(struct G_ENTRY));
  1486.             return;
  1487.          }
  1488.       }
  1489.       prevent->base.next = (struct G_OBJECT *)ent;
  1490.       ent->base.prev = (struct G_OBJECT *)prevent;
  1491.    }
  1492. }